home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / sd-26.zip / sdmoves.c < prev    next >
C/C++ Source or Header  |  1992-09-09  |  68KB  |  1,607 lines

  1. /* SD -- square dance caller's helper.
  2.  
  3.     Copyright (C) 1990, 1991, 1992  William B. Ackerman.
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 1, or (at your option)
  8.     any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.     This is for version 25. */
  20.  
  21. /* This defines the following functions:
  22.    canonicalize_rotation
  23.    reinstate_rotation
  24.    move
  25. */
  26.  
  27. #include "sd.h"
  28.  
  29.  
  30.  
  31. extern void canonicalize_rotation(setup *result) {
  32.  
  33.    if (result->kind == s_1x1) {
  34.       (void) copy_rot(result, 0, result, 0, (result->rotation & 3) * 011);
  35.       result->rotation = 0;
  36.    }
  37.    else if ((result->kind == s4x4) ||
  38.             (result->kind == s2x2) ||
  39.             (result->kind == s_star) ||
  40.             (result->kind == s_bigblob) ||
  41.             (result->kind == s_c1phan) ||
  42.             (result->kind == s_hyperglass) ||
  43.             (result->kind == s_galaxy)) {
  44.       /* The setup has 4-way symmetry.  We can canonicalize it so the
  45.          result rotation is zero. */
  46.       int i, rot, rot11, delta, bigd, i0, i1, i2, i3, j0, j1, j2, j3;
  47.       personrec x0, x1, x2, x3;
  48.  
  49.       rot = result->rotation & 3;
  50.       if (rot == 0) return;
  51.       rot11 = rot * 011;
  52.       bigd = setup_limits[result->kind] + 1;
  53.       delta = bigd >> 2;
  54.  
  55.       i0 = 1;
  56.       i1 = i0 + delta;
  57.       i2 = i1 + delta;
  58.       i3 = i2 + delta;
  59.       j0 = (rot-4)*delta+1;
  60.       j1 = j0 + delta;
  61.       j2 = j1 + delta;
  62.       j3 = j2 + delta;
  63.       for (i=0; i<delta; i++) {
  64.          if ((--i0) < 0) i0 += bigd;
  65.          if ((--i1) < 0) i1 += bigd;
  66.          if ((--i2) < 0) i2 += bigd;
  67.          if ((--i3) < 0) i3 += bigd;
  68.          if ((--j0) < 0) j0 += bigd;
  69.          if ((--j1) < 0) j1 += bigd;
  70.          if ((--j2) < 0) j2 += bigd;
  71.          if ((--j3) < 0) j3 += bigd;
  72.          x0 = result->people[i0];
  73.          x1 = result->people[i1];
  74.          x2 = result->people[i2];
  75.          x3 = result->people[i3];
  76.          result->people[j0].id1 = rotperson(x0.id1, rot11);
  77.          result->people[j0].id2 = x0.id2;
  78.          result->people[j1].id1 = rotperson(x1.id1, rot11);
  79.          result->people[j1].id2 = x1.id2;
  80.          result->people[j2].id1 = rotperson(x2.id1, rot11);
  81.          result->people[j2].id2 = x2.id2;
  82.          result->people[j3].id1 = rotperson(x3.id1, rot11);
  83.          result->people[j3].id2 = x3.id2;
  84.       }
  85.  
  86.       result->rotation = 0;
  87.    }
  88.    else if (result->kind == s_1x3) {
  89.       if (result->rotation & 2) {
  90.  
  91.          /* Must turn this setup upside-down. */
  92.    
  93.          swap_people(result, 0, 2);
  94.          (void) copy_rot(result, 0, result, 0, 022);
  95.          (void) copy_rot(result, 1, result, 1, 022);
  96.          (void) copy_rot(result, 2, result, 2, 022);
  97.       }
  98.       result->rotation &= 1;
  99.    }
  100.    else if (((setup_limits[result->kind] & ~07776) == 1)) {
  101.       /* We have a setup of an even number of people.  We know how to canonicalize
  102.          this.  The resulting rotation should be 0 or 1. */
  103.  
  104.       if (result->rotation & 2) {
  105.  
  106.          /* Must turn this setup upside-down. */
  107.    
  108.          int i, offs;
  109.    
  110.          offs = (setup_limits[result->kind]+1) >> 1;     /* Half the setup size. */
  111.    
  112.          for (i=0; i<offs; i++) {
  113.             swap_people(result, i, i+offs);
  114.             (void) copy_rot(result, i, result, i, 022);
  115.             (void) copy_rot(result, i+offs, result, i+offs, 022);
  116.          }
  117.       }
  118.       result->rotation &= 1;
  119.    }
  120.    else
  121.       result->rotation &= 3;
  122. }
  123.  
  124.  
  125. extern void reinstate_rotation(setup *ss, setup *result) {
  126.  
  127.    int globalrotation;
  128.  
  129.    switch (ss->kind) {
  130.       case s_normal_concentric:
  131.          globalrotation = 0;
  132.          break;
  133.       default:
  134.          globalrotation = ss->rotation;
  135.    }
  136.    
  137.    switch (result->kind) {
  138.       case s_normal_concentric:
  139.          result->inner.srotation += globalrotation;
  140.          result->outer.srotation += globalrotation;
  141.          break;
  142.       case nothing:
  143.          break;
  144.       default:
  145.          result->rotation += globalrotation;
  146.          break;
  147.    }
  148.  
  149.    canonicalize_rotation(result);
  150. }
  151.  
  152.  
  153.  
  154. typedef struct gloop {
  155.     int x;                  /* This person's coordinates, calibrated so that a matrix */
  156.     int y;                  /*   position cooresponds to an increase by 4. */
  157.     long_boolean sel;       /* True if this person is selected.  (False if selectors not in use.) */
  158.     long_boolean done;      /* Used for loop control on each pass */
  159.     long_boolean realdone;  /* Used for loop control on each pass */
  160.     int boybit;             /* 1 if boy, 0 if not (might be neither). */
  161.     int girlbit;            /* 1 if girl, 0 if not (might be neither). */ 
  162.     int dir;                /* This person's initial facing direction, 0 to 3. */
  163.     int deltax;             /* How this person will move, relative to his own facing */
  164.     int deltay;             /*   direction, when call is finally executed. */
  165.     int deltarot;           /* How this person will turn. */
  166.     int rollinfo;           /* How this person's roll info will be set. */
  167.     struct gloop *nextse;   /* Points to next person south (dir even) or east (dir odd.) */
  168.     struct gloop *nextnw;   /* Points to next person north (dir even) or west (dir odd.) */
  169.     long_boolean tbstopse;  /* True if nextse/nextnw is zero because the next spot */
  170.     long_boolean tbstopnw;  /*   is occupied by a T-boned person (as opposed to being empty.) */
  171.     } matrix_rec;
  172.  
  173.  
  174.  
  175.  
  176. /* This function is internal. */
  177.  
  178. static void start_matrix_call(
  179.    setup *ss,
  180.    int *nump,
  181.    matrix_rec matrix_info[],
  182.    long_boolean use_selector,
  183.    setup *people)
  184.  
  185. {
  186.    int i;
  187.    coordrec *thingyptr;
  188.  
  189.    clear_people(people);
  190.    
  191.    thingyptr = setup_coords[ss->kind];
  192.    if (!thingyptr) fail("Can't do this in this setup.");
  193.    
  194.    if (setup_limits[ss->kind] < 0) fail("Can't do this in this setup.");        /* this is actually superfluous */
  195.    
  196.    *nump = 0;
  197.    for (i=0; i<=setup_limits[ss->kind]; i++) {
  198.       if (ss->people[i].id1) {
  199.          if (*nump == 8) fail("?????too many people????");
  200.          (void) copy_person(people, *nump, ss, i);
  201.          matrix_info[*nump].x = thingyptr->xca[i];
  202.          matrix_info[*nump].y = thingyptr->yca[i];
  203.  
  204.          matrix_info[*nump].done = FALSE;
  205.          matrix_info[*nump].realdone = FALSE;
  206.  
  207.          if (use_selector)
  208.             matrix_info[*nump].sel = selectp(people, *nump);
  209.          else
  210.             matrix_info[*nump].sel = FALSE;
  211.  
  212.          matrix_info[*nump].dir = people->people[*nump].id1 & 3;
  213.    
  214.          matrix_info[*nump].girlbit = (people->people[*nump].id2 & ID2_GIRL) ? 1 : 0;
  215.          matrix_info[*nump].boybit = (people->people[*nump].id2 & ID2_BOY) ? 1 : 0;
  216.          matrix_info[*nump].nextse = 0;
  217.          matrix_info[*nump].nextnw = 0;
  218.          matrix_info[*nump].deltax = 0;
  219.          matrix_info[*nump].deltay = 0;
  220.          matrix_info[*nump].deltarot = 0;
  221.          matrix_info[*nump].rollinfo = ROLLBITM;
  222.          matrix_info[*nump].tbstopse = FALSE;
  223.          matrix_info[*nump].tbstopnw = FALSE;
  224.  
  225.          (*nump)++;
  226.       }
  227.    }
  228. }
  229.  
  230.  
  231. /* This function is internal. */
  232.  
  233. static void finish_matrix_call(
  234.    matrix_rec matrix_info[],
  235.    int nump,
  236.    setup *people,
  237.    setup *result)
  238.  
  239. {
  240.    int i, place;
  241.    int xmax, xpar, ymax, ypar, signature, x, y, k;
  242.    coordrec *checkptr;
  243.  
  244.    xmax = xpar = ymax = ypar = signature = 0;
  245.  
  246.    for (i=0; i<nump; i++) {
  247.       people->people[i].id1 = (rotperson(people->people[i].id1, matrix_info[i].deltarot*011) & (~ROLLBITS)) | matrix_info[i].rollinfo;
  248.  
  249.       /* If this person's position has low bit on, that means we consider his coordinates
  250.          not sufficiently well-defined that we will allow him to do any pressing or
  251.          trucking.  He is only allowed to turn.  That is, we will require deltax and
  252.          deltay to be zero.  An example of this situation is the points of a galaxy. */
  253.  
  254.       if (((matrix_info[i].x | matrix_info[i].y) & 1) && (matrix_info[i].deltax | matrix_info[i].deltay))
  255.          fail("Someone's ending position is not well defined.");
  256.  
  257.       switch (matrix_info[i].dir) {
  258.          case 0:
  259.             matrix_info[i].x += matrix_info[i].deltax;
  260.             matrix_info[i].y += matrix_info[i].deltay;
  261.             break;
  262.          case 1:
  263.             matrix_info[i].x += matrix_info[i].deltay;
  264.             matrix_info[i].y -= matrix_info[i].deltax;
  265.             break;
  266.          case 2:
  267.             matrix_info[i].x -= matrix_info[i].deltax;
  268.             matrix_info[i].y -= matrix_info[i].deltay;
  269.             break;
  270.          case 3:
  271.             matrix_info[i].x -= matrix_info[i].deltay;
  272.             matrix_info[i].y += matrix_info[i].deltax;
  273.             break;
  274.       }
  275.  
  276.       x = matrix_info[i].x;
  277.       y = matrix_info[i].y;
  278.  
  279.       /* Compute new max, parity, and signature info. */
  280.  
  281.       if ((x < 0) || ((x == 0) && (y < 0))) { x = -x; y = -y; }
  282.       signature |= 1 << ((31000 + 12*x - 11*y) % 31);
  283.       if (y < 0) y = -y;
  284.       /* Now x and y have both had absolute values taken. */
  285.       if (x > xmax) xmax = x;
  286.       if (y > ymax) ymax = y;
  287.       k = x | 4;
  288.       xpar |= (k & (~(k-1)));
  289.       k = y | 4;
  290.       ypar |= (k & (~(k-1)));
  291.    }
  292.  
  293.    ypar |= (xmax << 20) | (xpar << 16) | (ymax << 4);
  294.  
  295.    result->rotation = 0;
  296.  
  297.    if ((ypar == 0x00A20026) && ((signature & (~0x08008404)) == 0)) {
  298.       checkptr = setup_coords[s_rigger];
  299.       goto doit;
  300.    }
  301.    if ((ypar == 0x00770077) && ((signature & (~0x00418004)) == 0)) {
  302.       checkptr = setup_coords[s_galaxy];
  303.       goto doit;
  304.    }
  305.    else if ((ypar == 0x00A20026) && ((signature & (~0x01040420)) == 0)) {
  306.       checkptr = setup_coords[s_bone];
  307.       goto doit;
  308.    }
  309.    else if ((ypar == 0x00840026) && ((signature & (~0x04000308)) == 0)) {
  310.       checkptr = setup_coords[s_spindle];
  311.       goto doit;
  312.    }
  313.    else if ((ypar == 0x00A200A2) && ((signature & (~0x101CC4E6)) == 0)) {
  314.       checkptr = setup_coords[s_bigblob];
  315.       goto doit;
  316.    }
  317.    else if ((ypar == 0x00670055) && ((signature & (~0x01000420)) == 0)) {
  318.       checkptr = setup_coords[s_qtag];
  319.       goto doit;
  320.    }
  321.    else if ((ypar == 0x00550067) && ((signature & (~0x08410200)) == 0)) {
  322.       checkptr = setup_coords[s_qtag];
  323.       goto doitrot;
  324.    }
  325.    else if ((ypar == 0x00550057) && ((signature & (~0x20000620)) == 0)) {
  326.       checkptr = setup_coords[s_hrglass];
  327.       goto doit;
  328.    }
  329.    else if ((ypar == 0x00620044) && ((signature & (~0x11800C40)) == 0)) {
  330.       checkptr = setup_coords[s3x4];
  331.       goto doit;
  332.    }
  333.    else if ((ypar == 0x00440062) && ((signature & (~0x0C202300)) == 0)) {
  334.       checkptr = setup_coords[s3x4];
  335.       goto doitrot;
  336.    }
  337.    else if ((ypar == 0x00E20004) && ((signature & (~0x09002400)) == 0)) {
  338.       checkptr = setup_coords[s1x8];
  339.       goto doit;
  340.    }
  341.    else if ((ypar == 0x000400E2) && ((signature & (~0x08004202)) == 0)) {
  342.       checkptr = setup_coords[s1x8];
  343.       goto doitrot;
  344.    }
  345.    else if ((ypar == 0x00620022) && ((signature & (~0x00088006)) == 0)) {
  346.       checkptr = setup_coords[s2x4];
  347.       goto doit;
  348.    }
  349.    else if ((ypar == 0x00220062) && ((signature & (~0x10108004)) == 0)) {
  350.       checkptr = setup_coords[s2x4];
  351.       goto doitrot;
  352.    }
  353.    else if ((ypar == 0x00A20022) && ((signature & (~0x000C8026)) == 0)) {
  354.       checkptr = setup_coords[s2x6];
  355.       goto doit;
  356.    }
  357.    else if ((ypar == 0x002200A2) && ((signature & (~0x10108484)) == 0)) {
  358.       checkptr = setup_coords[s2x6];
  359.       goto doitrot;
  360.    }
  361.    else if ((ypar == 0x00E20022) && ((signature & (~0x004C8036)) == 0)) {
  362.       checkptr = setup_coords[s2x8];
  363.       goto doit;
  364.    }
  365.    else if ((ypar == 0x002200E2) && ((signature & (~0x12908484)) == 0)) {
  366.       checkptr = setup_coords[s2x8];
  367.       goto doitrot;
  368.    }
  369.    else if ((ypar == 0x00A20062) && ((signature & (~0x109CC067)) == 0)) {
  370.       checkptr = setup_coords[s4x6];
  371.       goto doit;
  372.    }
  373.    else if ((ypar == 0x006200A2) && ((signature & (~0x1918C4C6)) == 0)) {
  374.       checkptr = setup_coords[s4x6];
  375.       goto doitrot;
  376.    }
  377.    else if ((ypar == 0x00620062) && ((signature & (~0x1018C046)) == 0)) {
  378.       checkptr = setup_coords[s4x4];
  379.       goto doit;
  380.    }
  381.    /* **** These last ones are sort of a crock.  They are designed to make
  382.       matrix calls work in distorted or virtual setups in some circumstances
  383.       (i.e. if no one changes coordinates.)  However, they won't work in the
  384.       presence of unsymmetrical phantoms.  What we really should do is, if
  385.       the setup is virtual/distorted (or maybe the test should be if no one
  386.       moved) just force people back to the same setup they started in. */
  387.    else if ((ypar == 0x00220022) && ((signature & (~0x00008004)) == 0)) {
  388.       checkptr = setup_coords[s2x2];
  389.       goto doit;
  390.    }
  391.    else if ((ypar == 0x00620004) && ((signature & (~0x01000400)) == 0)) {
  392.       checkptr = setup_coords[s1x4];
  393.       goto doit;
  394.    }
  395.    else if ((ypar == 0x00220004) && ((signature & (~0x01000000)) == 0)) {
  396.       checkptr = setup_coords[s_1x2];
  397.       goto doit;
  398.    }
  399.  
  400.    fail("Can't handle this result matrix.");
  401.  
  402.    
  403.  
  404. doit:
  405.       result->kind = checkptr->result_kind;
  406.       for (i=0; i<nump; i++) {
  407.          place = checkptr->numbers[3-(matrix_info[i].y >> 2)] [(matrix_info[i].x >> 2)+4];
  408.          if (place < 0) fail("Person has moved into a grossly ill-defined location.");
  409.          if ((checkptr->xca[place] != matrix_info[i].x) || (checkptr->yca[place] != matrix_info[i].y))
  410.             fail("Person has moved into a slightly ill-defined location.");
  411.          install_person(result, place, people, i);
  412.       }
  413.       
  414.       return;
  415.  
  416. doitrot:
  417.       result->kind = checkptr->result_kind;
  418.       result->rotation = 1;   
  419.       for (i=0; i<nump; i++) {
  420.          place = checkptr->numbers[3-(matrix_info[i].x >> 2)] [3-(matrix_info[i].y >> 2)];
  421.          if (place < 0) fail("Person has moved into a grossly ill-defined location.");
  422.          if ((checkptr->xca[place] != -matrix_info[i].y) || (checkptr->yca[place] != matrix_info[i].x))
  423.             fail("Person has moved into a slightly ill-defined location.");
  424.          install_rot(result, place, people, i, 033);
  425.       }
  426.       
  427.       return;
  428. }
  429.  
  430.  
  431.  
  432. /* This function is internal. */
  433.  
  434. static void matrixmove(
  435.    setup *ss,
  436.    callspec_block *callspec,
  437.    setup *result)
  438.  
  439. {
  440.    int datum;
  441.    setup people;
  442.    matrix_rec matrix_info[9];
  443.    int i, nump, alldelta;
  444.  
  445.    alldelta = 0;
  446.  
  447.    start_matrix_call(ss, &nump, matrix_info, TRUE, &people);
  448.  
  449.    for (i=0; i<nump; i++) {
  450.       if (matrix_info[i].sel) {
  451.          /* This is legal if girlbit or boybit is on (in which case we use the appropriate datum)
  452.             or if the two data are identical so the sex doesn't matter. */
  453.          if ((matrix_info[i].girlbit | matrix_info[i].boybit) == 0 &&
  454.                   (callspec->stuff.matrix.stuff[0] != callspec->stuff.matrix.stuff[1]))
  455.             fail("Can't determine sex of this person.");
  456.  
  457.          datum = callspec->stuff.matrix.stuff[matrix_info[i].girlbit];
  458.          alldelta |= (  matrix_info[i].deltax = ( ((datum >> 7) & 0x1F) - 16) << 1  );
  459.          alldelta |= (  matrix_info[i].deltay = ( ((datum >> 2) & 0x1F) - 16) << 1  );
  460.          matrix_info[i].deltarot = datum & 03;
  461.          matrix_info[i].rollinfo = (datum >> 12) * ROLLBITR;
  462.       }
  463.    }
  464.  
  465.    if ((alldelta != 0) && (ss->setupflags & SETUPFLAG__DISTORTED))
  466.       fail("This call not allowed in distorted or virtual setup.");
  467.    
  468.    finish_matrix_call(matrix_info, nump, &people, result);
  469. }
  470.  
  471.  
  472. /* This function is internal. */
  473.  
  474. static void do_pair(
  475.    matrix_rec *ppp,        /* Selected person */
  476.    matrix_rec *qqq,        /* Unselected person */
  477.    callspec_block *callspec,
  478.    int flip,
  479.    int filter)             /* 1 to do N/S facers, 0 for E/W facers. */
  480. {
  481.    int base;
  482.    int datum;
  483.    int flags;
  484.  
  485.    flags = callspec->stuff.matrix.flags;
  486.  
  487.    if ((filter ^ ppp->dir) & 1) {
  488.       base = (ppp->dir & 2) ? 6 : 4;
  489.       if (!(flags & MTX_USE_SELECTOR)) base &= 3;
  490.       base ^= flip;
  491.  
  492.       /* This is legal if girlbit or boybit is on (in which case we use the appropriate datum)
  493.          or if the two data are identical so the sex doesn't matter. */
  494.       if ((ppp->girlbit | ppp->boybit) == 0 &&
  495.                (callspec->stuff.matrix.stuff[base] != callspec->stuff.matrix.stuff[base+1]))
  496.          fail("Can't determine sex of this person.");
  497.  
  498.       datum = callspec->stuff.matrix.stuff[base+ppp->girlbit];
  499.       if (datum == 0) fail("Can't do this call.");
  500.    
  501.       ppp->deltax = (((datum >> 7) & 0x1F) - 16) << 1;
  502.       ppp->deltay = (((datum >> 2) & 0x1F) - 16) << 1;
  503.       ppp->deltarot = datum & 3;
  504.       ppp->rollinfo = (datum >> 12) * ROLLBITR;
  505.       ppp->realdone = TRUE;
  506.    }
  507.    ppp->done = TRUE;
  508.  
  509.    if ((filter ^ qqq->dir) & 1) {
  510.       base = (qqq->dir & 2) ? 0 : 2;
  511.       if (flags & MTX_IGNORE_NONSELECTEES) base |= 4;
  512.       base ^= flip;
  513.  
  514.       /* This is legal if girlbit or boybit is on (in which case we use the appropriate datum)
  515.          or if the two data are identical so the sex doesn't matter. */
  516.       if ((qqq->girlbit | qqq->boybit) == 0 &&
  517.                (callspec->stuff.matrix.stuff[base] != callspec->stuff.matrix.stuff[base+1]))
  518.          fail("Can't determine sex of this person.");
  519.  
  520.       datum = callspec->stuff.matrix.stuff[base+qqq->girlbit];
  521.       if (datum == 0) fail("Can't do this call.");
  522.  
  523.       qqq->deltax = (((datum >> 7) & 0x1F) - 16) << 1;
  524.       qqq->deltay = (((datum >> 2) & 0x1F) - 16) << 1;
  525.       qqq->deltarot = datum & 3;
  526.       qqq->rollinfo = (datum >> 12) * ROLLBITR;
  527.       qqq->realdone = TRUE;
  528.    }
  529.    qqq->done = TRUE;
  530. }
  531.  
  532.  
  533. /* This function is internal. */
  534.  
  535. static void do_matrix_chains(
  536.    matrix_rec matrix_info[],
  537.    int nump,
  538.    callspec_block *callspec,
  539.    int filter)                        /* 1 for E/W chains, 0 for N/S chains. */
  540.  
  541. {
  542.    long_boolean another_round;
  543.    int i, j, flags;
  544.  
  545.    flags = callspec->stuff.matrix.flags;
  546.  
  547.    /* Find adjacency relationships, and fill in the "se"/"nw" pointers. */
  548.  
  549.    for (i=0; i<nump; i++) {
  550.       if ((flags & MTX_IGNORE_NONSELECTEES) && (!matrix_info[i].sel)) continue;
  551.       for (j=0; j<nump; j++) {
  552.          if ((flags & MTX_IGNORE_NONSELECTEES) && (!matrix_info[j].sel)) continue;
  553.          /* Find out if these people are adjacent in the right way. */
  554.  
  555.          if (    ( filter && matrix_info[j].x == matrix_info[i].x + 4 && matrix_info[j].y == matrix_info[i].y)
  556.             ||   (!filter && matrix_info[j].y == matrix_info[i].y - 4 && matrix_info[j].x == matrix_info[i].x)   ) {
  557.  
  558.             /* Now, if filter = 1, person j is just east of person i.
  559.                If filter = 0, person j is just south of person i. */
  560.  
  561.             if (flags & MTX_TBONE_IS_OK) {
  562.                matrix_info[i].nextse = &matrix_info[j];     /* Make the chain independently of facing direction. */
  563.                matrix_info[j].nextnw = &matrix_info[i];
  564.             }
  565.             else {
  566.                if ((matrix_info[i].dir ^ filter) & 1) {
  567.                   if ((matrix_info[i].dir ^ matrix_info[j].dir) & 1) {
  568.                      if (!(flags & MTX_STOP_AND_WARN_ON_TBONE)) fail("People are T-boned.");
  569.                      matrix_info[i].tbstopse = TRUE;
  570.                      matrix_info[j].tbstopnw = TRUE;
  571.                   }
  572.                   else {
  573.                      if ((flags & MTX_MUST_FACE_SAME_WAY) && (matrix_info[i].dir ^ matrix_info[j].dir))
  574.                         fail("Paired people must face the same way.");
  575.                      matrix_info[i].nextse = &matrix_info[j];
  576.                      matrix_info[j].nextnw = &matrix_info[i];
  577.                   }
  578.                }
  579.             }
  580.             break;
  581.          }
  582.       }
  583.    }
  584.  
  585.    /* Pick out pairs of people and move them. */
  586.  
  587.    another_round = TRUE;
  588.  
  589.    while (another_round) {
  590.       another_round = FALSE;
  591.  
  592.       for (i=0; i<nump; i++) {
  593.          if (!matrix_info[i].done) {
  594.  
  595.             /* This person might be ready to be paired up with someone. */
  596.  
  597.             if (matrix_info[i].nextse) {
  598.                if (matrix_info[i].nextnw)
  599.                   /* This person has neighbors on both sides.  Can't do anything yet. */
  600.                   ;
  601.                else {
  602.                   /* This person has a neighbor on south/east side only. */
  603.  
  604.                   if (matrix_info[i].tbstopnw) warn(warn__not_tbone_person);
  605.  
  606.                   if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   ) {
  607.                      if ((!(flags & MTX_IGNORE_NONSELECTEES)) && matrix_info[i].nextse->sel) {
  608.                         fail("Two adjacent selected people.");
  609.                      }
  610.                      else {
  611.  
  612.                         /* Do this pair.  First, chop the pair off from anyone else. */
  613.  
  614.                         if (matrix_info[i].nextse->nextse) matrix_info[i].nextse->nextse->nextnw = 0;
  615.                         matrix_info[i].nextse->nextse = 0;
  616.                         another_round = TRUE;
  617.                         do_pair(&matrix_info[i], matrix_info[i].nextse, callspec, 0, filter);
  618.                      }
  619.                   }
  620.                }
  621.             }
  622.             else {
  623.                if (matrix_info[i].nextnw) {
  624.                   /* This person has a neighbor on north/west side only. */
  625.  
  626.                   if (matrix_info[i].tbstopse) warn(warn__not_tbone_person);
  627.  
  628.                   if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   ) {
  629.                      if ((!(flags & MTX_IGNORE_NONSELECTEES)) && matrix_info[i].nextnw->sel) {
  630.                         fail("Two adjacent selected people.");
  631.                      }
  632.                      else {
  633.                         /* Do this pair.  First, chop the pair off from anyone else. */
  634.  
  635.                         if (matrix_info[i].nextnw->nextnw) matrix_info[i].nextnw->nextnw->nextse = 0;
  636.                         matrix_info[i].nextnw->nextnw = 0;
  637.                         another_round = TRUE;
  638.                         do_pair(&matrix_info[i], matrix_info[i].nextnw, callspec, 2, filter);
  639.                      }
  640.                   }
  641.                }
  642.                else {
  643.                   /* Person is alone.  If this is his lateral axis, mark him done and don't move him. */
  644.  
  645.                   if ((matrix_info[i].dir ^ filter) & 1) {
  646.                      if (matrix_info[i].tbstopse || matrix_info[i].tbstopnw) warn(warn__not_tbone_person);
  647.                      if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   )
  648.                         fail("Person has no one to work with.");
  649.                      matrix_info[i].done = TRUE;
  650.                   }
  651.                }
  652.             }
  653.          }
  654.       }
  655.    }
  656. }
  657.  
  658.  
  659.  
  660.  
  661.  
  662.  
  663. /* This function is internal. */
  664.  
  665. static void partner_matrixmove(
  666.    setup *ss,
  667.    callspec_block *callspec,
  668.    setup *result)
  669.  
  670. {
  671.    int flags;
  672.    setup people;
  673.    matrix_rec matrix_info[9];
  674.    int i, nump;
  675.  
  676.    if (ss->setupflags & SETUPFLAG__DISTORTED)
  677.       fail("This call not allowed in distorted or virtual setup.");
  678.  
  679.    flags = callspec->stuff.matrix.flags;
  680.  
  681.    start_matrix_call(ss, &nump, matrix_info, flags & MTX_USE_SELECTOR, &people);
  682.  
  683.    /* Make the lateral chains first. */
  684.  
  685.    do_matrix_chains(matrix_info, nump, callspec, 1);
  686.  
  687.    /* Now clean off the pointers in preparation for the second pass. */
  688.  
  689.    for (i=0; i<nump; i++) {
  690.       matrix_info[i].done = FALSE;
  691.       matrix_info[i].nextse = 0;
  692.       matrix_info[i].nextnw = 0;
  693.       matrix_info[i].tbstopse = FALSE;
  694.       matrix_info[i].tbstopnw = FALSE;
  695.    }
  696.  
  697.    /* Vertical chains next. */
  698.  
  699.    do_matrix_chains(matrix_info, nump, callspec, 0);
  700.  
  701.    /* Scan for people who ought to have done something but didn't. */
  702.  
  703.    for (i=0; i<nump; i++) {
  704.       if (!matrix_info[i].realdone) {
  705.          if (   (!(flags & MTX_USE_SELECTOR))  ||  matrix_info[i].sel   ) {
  706.             fail("Person could not identify other person to work with.");
  707.          }
  708.       }
  709.    }
  710.  
  711.    finish_matrix_call(matrix_info, nump, &people, result);
  712. }
  713.  
  714.  
  715. /* This function is internal. */
  716.  
  717. static void rollmove(
  718.    setup *ss,
  719.    callspec_block *callspec,
  720.    setup *result)
  721.  
  722. {
  723.    int i, rot;
  724.  
  725.    if (setup_limits[ss->kind] < 0) fail("Can't roll in this setup.");
  726.    
  727.    result->kind = ss->kind;
  728.    result->rotation = ss->rotation;
  729.    
  730.    for (i=0; i<=setup_limits[ss->kind]; i++) {
  731.       if (ss->people[i].id1) {
  732.          rot = 0;
  733.          if (!(callspec->callflags & cflag__requires_selector) || selectp(ss, i)) {
  734.             switch (ss->people[i].id1 & ROLLBITS) {
  735.                case ROLLBITL: rot = 033; break;
  736.                case ROLLBITM: break;
  737.                case ROLLBITR: rot = 011; break;
  738.                default: fail("Roll not supported after previous call.");
  739.             }
  740.          }
  741.          install_rot(result, i, ss, i, rot);
  742.       }
  743.       else
  744.          clear_person(result, i);
  745.    }
  746. }
  747.  
  748.  
  749. /* Strip out those concepts that do not have the "dfm__xxx" flag set saying that they are to be
  750.    inherited to this part of the call.  BUT: the "dfm_inherit_left" (a.k.a. "FINAL__LEFT") flag controls
  751.    both "FINAL__REVERSE" and "FINAL__LEFT", turning the former into the latter.  This makes reverse
  752.    circle by, touch by, and clean sweep work. */
  753.  
  754. static final_set get_mods_for_subcall(final_set new_final_concepts, defmodset this_mod, int callflags)
  755. {
  756.    final_set retval;
  757.  
  758.    retval = new_final_concepts;
  759.  
  760.    /* If this subcall has "inherit_reverse" or "inherit_left" given, but the top-level call
  761.       doesn't permit the corresponding flag to be given, we should turn any "reverse" or
  762.       "left" modifier that was given into the other one, and cause that to be inherited.
  763.       This is what turns, for example, part 3 of "*REVERSE* clean sweep" into a "*LEFT* 1/2 tag". */
  764.  
  765.  
  766.    if (this_mod & ~callflags & (FINAL__REVERSE | FINAL__LEFT)) {
  767.       if (new_final_concepts & (FINAL__REVERSE | FINAL__LEFT))
  768.          retval |= (FINAL__REVERSE | FINAL__LEFT);
  769.    }
  770.  
  771.    retval &= ~(new_final_concepts & HERITABLE_FLAG_MASK & ~this_mod);
  772.    
  773.    return (retval);
  774. }
  775.  
  776.  
  777.  
  778. /* ***** The following is all wrong.  It's much simpler now.  We just fill in the
  779.          elongation in the result flags, if we can deduce it. */
  780. /* Whenever the ending setup is 2x2, we are responsible for filling in the "parallel_conc_end"
  781.    field of the result, to say, for 1x4/dmd->2x2 calls, whether the people prefer to
  782.    elongate parallel to the long axis of the 1x4/dmd, and, for 2x2->2x2 calls, whether
  783.    the people prefer to go to antispots.  This info will be used if the call is executed
  784.    by the outsides, so that the resulting 2x2 must be elongated in some direction to leave
  785.    room for the centers.  Of course, if the "concentric" or "checkpoint" concept has been
  786.    given, this info will be overruled by the various rules for executing those concepts.
  787.    If this call is executed as part of some kind of concentric schema, there are modification
  788.    flags that can also override this stuff. */
  789.  
  790.  
  791. extern void move(
  792.    setup *ss,
  793.    parse_block *parseptr,
  794.    callspec_block *callspec,
  795.    final_set final_concepts,
  796.    long_boolean qtfudged,
  797.    setup *result)
  798.  
  799. {
  800.    int subcall_index;
  801.    final_set temp_concepts, conc1, conc2;
  802.    long_boolean qtf;
  803.    parse_block *cp1;
  804.    parse_block *cp2;
  805.    warning_info saved_warnings;
  806.    int tbonetest;
  807.    setup tempsetup;
  808.    parse_block *saved_magic_diamond;
  809.    final_set new_final_concepts;
  810.    final_set check_concepts;
  811.    parse_block *parseptrcopy;
  812.    callspec_block *call1, *call2;
  813.    calldef_schema the_schema;
  814.    long_boolean mirror;
  815.  
  816.    clear_people(result);
  817.    result->setupflags = 0;
  818.  
  819.    if (!callspec) {
  820.  
  821.       /* Scan the "final" concepts, remembering them and their end point. */
  822.       last_magic_diamond = 0;
  823.       parseptrcopy = process_final_concepts(parseptr, TRUE, &new_final_concepts);
  824.       saved_magic_diamond = last_magic_diamond;
  825.  
  826.       /* See if there were any "non-final" ones present also. */
  827.  
  828.       new_final_concepts |= final_concepts;         /* Include any old ones we had. */
  829.  
  830.       /* These are the concepts that we are interested in. */
  831.  
  832.       check_concepts = new_final_concepts & ~(FINAL__MUST_BE_TAG | FINAL__MUST_BE_SCOOT);
  833.  
  834.       if (parseptrcopy->concept->kind > marker_end_of_list) {
  835.  
  836.          /* We now know that there are "non-final" (virtual setup) concepts present. */
  837.  
  838.          /* Look for virtual setup concept that can be done by dispatch from table, with no
  839.             intervening final concepts. */
  840.  
  841.          if ((check_concepts == 0) && do_big_concept(ss, parseptrcopy, result)) {
  842.             goto norot;
  843.          }
  844.  
  845.          /* We are about to do some semi-final (?) concepts.  Stepping to a wave or
  846.             rearing back from one is no longer permitted. */
  847.          ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;
  848.  
  849.          /* There are a few "final" concepts that
  850.             will not be treated as such if there are non-final concepts occurring
  851.             after them.  Instead, they will be treated as virtual setup concepts.
  852.             This is what makes "magic once removed trade" work, for
  853.             example.  On the other hand, if there are no non-final concepts following, treat these as final.
  854.             This is what makes "magic transfer" or "split square thru" work. */
  855.  
  856.          if (check_concepts == FINAL__SPLIT) {
  857.             map_thing *split_map;
  858.       
  859.             ss->setupflags |= SETUPFLAG__SAID_SPLIT;
  860.  
  861.             if (ss->kind == s2x4) split_map = (*map_lists[s2x2][1])[MPKIND__SPLIT][0];
  862.             else if (ss->kind == s1x8) split_map = (*map_lists[s1x4][1])[MPKIND__SPLIT][0];
  863.             else if (ss->kind == s_ptpd) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][0];
  864.             else if (ss->kind == s_qtag) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][1];
  865.             else fail("Can't do split concept in this setup.");
  866.       
  867.             divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__SPLIT,
  868.                split_map, phantest_ok, TRUE, result);
  869.          }
  870.          else if ((check_concepts & ~FINAL__DIAMOND) == FINAL__MAGIC) {
  871.             if (ss->kind == s2x4) {
  872.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__MAGIC,
  873.                   &map_2x4_magic, phantest_ok, TRUE, result);
  874.             }
  875.             else if (ss->kind == s_qtag) {
  876.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__MAGIC,
  877.                   &map_qtg_magic, phantest_ok, TRUE, result);
  878.  
  879.                /* Since more concepts follow the magic and/or interlocked stuff, we can't
  880.                   allow the concept to be just "magic" etc.  We have to change it to
  881.                   "magic diamond, ..."  Otherwise, things could come out sounding like
  882.                   "magic diamond as couples quarter right" when we should really be saying
  883.                   "magic diamond, diamond as couples quarter right".  Therefore, we are going
  884.                   to do something seriously hokey: we are going to change the concept descriptor
  885.                   to one whose name has the extra "diamond" word.  We do this by marking the
  886.                   setupflags word in the result.  The actual hokey stuff will be done presently. */
  887.  
  888.                result->setupflags |= RESULTFLAG__NEED_DIAMOND;
  889.             }
  890.             else if (ss->kind == s_ptpd) {
  891.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__MAGIC,
  892.                   &map_ptp_magic, phantest_ok, TRUE, result);
  893.                result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  894.             }
  895.             else
  896.                fail("Can't do magic concept in this context.");
  897.          }
  898.          else if ((check_concepts & ~FINAL__DIAMOND) == FINAL__INTERLOCKED) {
  899.             if (ss->kind == s_qtag) {
  900.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__INTERLOCKED,
  901.                   &map_qtg_intlk, phantest_ok, TRUE, result);
  902.                result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  903.             }
  904.             if (ss->kind == s_ptpd) {
  905.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__INTERLOCKED,
  906.                   &map_ptp_intlk, phantest_ok, TRUE, result);
  907.                result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  908.             }
  909.             else
  910.                fail("Can't do interlocked concept in this context.");
  911.          }
  912.          else if ((check_concepts & ~FINAL__DIAMOND) == (FINAL__INTERLOCKED | FINAL__MAGIC)) {
  913.             if (ss->kind == s_qtag) {
  914.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~(FINAL__INTERLOCKED | FINAL__MAGIC),
  915.                      &map_qtg_magic_intlk, phantest_ok, TRUE, result);
  916.                result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  917.             }
  918.             if (ss->kind == s_ptpd) {
  919.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~(FINAL__INTERLOCKED | FINAL__MAGIC),
  920.                      &map_ptp_magic_intlk, phantest_ok, TRUE, result);
  921.                result->setupflags |= RESULTFLAG__NEED_DIAMOND;      /* See above. */
  922.             }
  923.             else
  924.                fail("Can't do magic interlocked concept in this context.");
  925.          }
  926.          else if (check_concepts == FINAL__DIAMOND) {
  927.             if (ss->kind == sdmd) {
  928.                divided_setup_move(ss, parseptrcopy, NULLCALLSPEC, new_final_concepts & ~FINAL__DIAMOND,
  929.                      (*map_lists[s_1x2][1])[MPKIND__DMD_STUFF][0], phantest_ok, TRUE, result);
  930.             }
  931.             else if (ss->kind == s_qtag) {
  932.                /* Divide into diamonds and try again.  (Note that we back up the concept pointer.) */
  933.                divided_setup_move(ss, parseptr, NULLCALLSPEC, 0,
  934.                      (*map_lists[sdmd][1])[MPKIND__SPLIT][1], phantest_ok, FALSE, result);
  935.             }
  936.             else
  937.                fail("Must have diamonds for this concept.");
  938.          }
  939.          else
  940.             fail2("Can't do this concept with other concepts preceding it:", parseptrcopy->concept->name);
  941.       }
  942.       else {
  943.          /* There are no "non-final" concepts.  The only concepts are the final ones that
  944.             have been encoded into new_final_concepts. */
  945.  
  946.          /* We must read the selector out of the concept list and use it for this call to "move".
  947.             We are effectively using it as an argument to "move", with all the care that must go
  948.             into invocations of recursive procedures.  However, at its point of actual use, it
  949.             must be in a global variable.  Therefore, we explicitly save and restore that
  950.             global variable (in a dynamic variable local to this instance) rather than passing
  951.             it as an explicit argument.  By saving it and restoring it in this way, we make
  952.             things like "checkpoint bounce the beaux by bounce the belles" work. */
  953.          
  954.          {
  955.             selector_kind saved_selector = current_selector;
  956.    
  957.             current_selector = parseptrcopy->selector;
  958.             move(ss, parseptrcopy, parseptrcopy->call, new_final_concepts, qtfudged, result);
  959.             current_selector = saved_selector;
  960.          }
  961.       }
  962.  
  963.       /* If execution of the call raised a request that we change a concept name from "magic" to
  964.          "magic diamond,", for example, do so. */
  965.  
  966.       if (result->setupflags & RESULTFLAG__NEED_DIAMOND) {
  967.          if (saved_magic_diamond && saved_magic_diamond->concept->value.arg1 == 0) {
  968.             if (saved_magic_diamond->concept->kind == concept_magic) saved_magic_diamond->concept = &special_magic;
  969.             else if (saved_magic_diamond->concept->kind == concept_interlocked) saved_magic_diamond->concept = &special_interlocked;
  970.          }
  971.       }
  972.  
  973.       return;
  974.    }
  975.    
  976.    /* We have a genuine call.  Presumably all serious concepts have been disposed of
  977.       (that is, nothing interesting will be found in parseptr -- it might be
  978.       useful to check that someday) and we just have the callspec and the final
  979.       concepts.
  980.       The first thing we must do is check for a call whose schema is single (cross)
  981.       concentric.  If so, be sure the setup is divided into 1x4's or diamonds. */
  982.  
  983.    the_schema = callspec->schema;
  984.    if (the_schema == schema_maybe_single_concentric)
  985.       the_schema = (final_concepts & FINAL__SINGLE) ? schema_single_concentric : schema_concentric;
  986.    else if (the_schema == schema_maybe_matrix_conc_star)
  987.       the_schema = (final_concepts & FINAL__12_MATRIX) ? schema_conc_star12 : schema_conc_star;
  988.  
  989.    /* Do some quick error checking for visible fractions.  For now, either flag is acceptable.  Later, we will
  990.       distinguish between the "visible_fractions" and "first_part_visible" flags. */
  991.    if ((ss->setupflags & SETUPFLAG__FRACTIONALIZE_MASK) &&
  992.             (((the_schema != schema_sequential) && (the_schema != schema_split_sequential)) || (!(callspec->callflags & (cflag__visible_fractions | cflag__first_part_visible)))))
  993.       fail("This call can't be fractionalized.");
  994.  
  995.    switch (the_schema) {
  996.       case schema_single_concentric:
  997.       case schema_single_cross_concentric:
  998.          switch (ss->kind) {
  999.             case s2x4:
  1000.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[s1x4][1])[MPKIND__SPLIT][1], phantest_ok, TRUE, result);
  1001.                return;
  1002.             case s1x8:
  1003.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[s1x4][1])[MPKIND__SPLIT][0], phantest_ok, TRUE, result);
  1004.                return;
  1005.             case s_qtag:
  1006.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[sdmd][1])[MPKIND__SPLIT][1], phantest_ok, TRUE, result);
  1007.                return;
  1008.             case s_ptpd:
  1009.                divided_setup_move(ss, parseptr, callspec, final_concepts, (*map_lists[sdmd][1])[MPKIND__SPLIT][0], phantest_ok, TRUE, result);
  1010.                return;
  1011.          }
  1012.    }
  1013.  
  1014.    /* If the "diamond" concept has been given and the call doesn't want it, we do
  1015.       the "diamond single wheel" variety. */
  1016.  
  1017.    if (FINAL__DIAMOND & final_concepts & (~callspec->callflags))  {
  1018.       /* If the call is sequentially or concentrically defined, the top level flag is required
  1019.          before the diamond concept can be inherited.  Since that flag is off, it is an error. */
  1020.       if (the_schema != schema_by_array)
  1021.          fail("Can't do this call with the 'diamond' concept.");
  1022.  
  1023.       switch (ss->kind) {
  1024.          case sdmd:
  1025.             divided_setup_move(ss, parseptr, callspec, final_concepts & ~FINAL__DIAMOND,
  1026.                   (*map_lists[s_1x2][1])[MPKIND__DMD_STUFF][0], phantest_ok, TRUE, result);
  1027.             return;
  1028.          case s_qtag:
  1029.             /* If in a qtag, perhaps we ought to divide into single diamonds and try again.
  1030.                BUT: if "magic" or "interlocked" is also present, we don't.  We let basic_move deal with
  1031.                it.  It will come back here after it has done what it needs to. */
  1032.    
  1033.             if ((final_concepts & (FINAL__MAGIC | FINAL__INTERLOCKED)) == 0) {
  1034.                /* Divide into diamonds and try again.  Note that we do not clear the concept. */
  1035.                divided_setup_move(ss, parseptr, callspec, final_concepts,
  1036.                      (*map_lists[sdmd][1])[MPKIND__SPLIT][1], phantest_ok, FALSE, result);
  1037.                return;
  1038.             }
  1039.             break;
  1040.          case s_ptpd:
  1041.             /* If in point-to-point diamonds, perhaps we ought to divide into single diamonds and try again.
  1042.                BUT: if "magic" or "interlocked" is also present, we don't.  We let basic_move deal with
  1043.                it.  It will come back here after it has done what it needs to. */
  1044.    
  1045.             if ((final_concepts & (FINAL__MAGIC | FINAL__INTERLOCKED)) == 0) {
  1046.                /* Divide into diamonds and try again.  Note that we do not clear the concept. */
  1047.                divided_setup_move(ss, parseptr, callspec, final_concepts,
  1048.                      (*map_lists[sdmd][1])[MPKIND__SPLIT][0], phantest_ok, FALSE, result);
  1049.                return;
  1050.             }
  1051.             break;
  1052.          default: fail("Must have diamonds for this concept.");
  1053.       }
  1054.    }
  1055.  
  1056.    mirror = FALSE;
  1057.  
  1058.    /* It may be appropriate to step to a wave or rear back from one.
  1059.       This is only legal if the flag forbidding same is off.
  1060.       Furthermore, if certain modifiers have been given, we don't allow it. */
  1061.  
  1062.    if (final_concepts & (FINAL__MAGIC | FINAL__INTERLOCKED | FINAL__12_MATRIX | FINAL__FUNNY))
  1063.       ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;
  1064.  
  1065.    /* But, alas, if fractionalization is on, we can't do it yet, because we don't
  1066.       know whether we are starting at the beginning.  In the case of fractionalization,
  1067.       we will do it later.  We already know that the call is sequentially defined. */
  1068.  
  1069.    if ((!(ss->setupflags & (SETUPFLAG__NO_STEP_TO_WAVE | SETUPFLAG__FRACTIONALIZE_MASK))) &&
  1070.          (callspec->callflags & (cflag__rear_back_from_r_wave | cflag__rear_back_from_qtag | cflag__step_to_wave))) {
  1071.  
  1072.       ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;  /* Can only do it once. */
  1073.  
  1074.       if (final_concepts & FINAL__LEFT) {
  1075.          mirror = TRUE;
  1076.          mirror_this(ss);
  1077.       }
  1078.  
  1079.       if (setup_limits[ss->kind] >= 0)          /* We don't understand absurd setups. */
  1080.          touch_or_rear_back(ss, callspec->callflags);
  1081.    }
  1082.  
  1083.    if (ss->setupflags & SETUPFLAG__DOING_ENDS) {
  1084.       /* Check for special case of ends doing a call like "detour" which specifically
  1085.          allows just the ends part to be done. */
  1086.       if ((the_schema == schema_concentric || the_schema == schema_rev_checkpoint) &&
  1087.             (dfm_endscando & callspec->stuff.conc.outerdef.modifiers)) {
  1088.  
  1089.          /* Copy the concentricity flags from the call definition into the setup.  All the fuss
  1090.             in database.h about concentricity flags co-existing with setupflags refers
  1091.             to this moment. */
  1092.          ss->setupflags |= (callspec->stuff.conc.outerdef.modifiers & DFM_CONCENTRICITY_FLAG_MASK);
  1093.  
  1094.          callspec = base_calls[callspec->stuff.conc.outerdef.call_id];
  1095.          the_schema = callspec->schema;
  1096.          if (the_schema == schema_maybe_single_concentric)
  1097.             the_schema = (final_concepts & FINAL__SINGLE) ? schema_single_concentric : schema_concentric;
  1098.          else if (the_schema == schema_maybe_matrix_conc_star)
  1099.             the_schema = (final_concepts & FINAL__12_MATRIX) ? schema_conc_star12 : schema_conc_star;
  1100.       }
  1101.    }
  1102.  
  1103.    /* Enforce the restriction that only tagging or scooting calls are allowed in certain contexts. */
  1104.  
  1105.    if ((final_concepts & FINAL__MUST_BE_TAG) && (!(cflag__is_tag_call & callspec->callflags)))
  1106.       fail("Only a tagging call is allowed here.");
  1107.    else if ((final_concepts & FINAL__MUST_BE_SCOOT) && (!(cflag__is_scoot_call & callspec->callflags)))
  1108.       fail("Only a scoot/tag (chain thru) (and scatter) call is allowed here.");
  1109.  
  1110.    final_concepts &= ~(FINAL__MUST_BE_TAG | FINAL__MUST_BE_SCOOT);
  1111.  
  1112.    /* If the "split" concept has been given and this call uses that concept for a special
  1113.       meaning (split square thru, split dixie style), set the special flag to determine that
  1114.       action, and remove the split concept.  Why remove it?  So that "heads split catch grand
  1115.       mix 3" will work.  If we are doing a "split catch", we don't really want to split the
  1116.       setup into 2x2's that are isolated from each other. */
  1117.  
  1118.    if (final_concepts & FINAL__SPLIT) {
  1119.       if (callspec->callflags & cflag__split_like_square_thru)
  1120.          final_concepts = (final_concepts | FINAL__SPLIT_SQUARE_APPROVED) & (~FINAL__SPLIT);
  1121.       else if (callspec->callflags & cflag__split_like_dixie_style)
  1122.          final_concepts = (final_concepts | FINAL__SPLIT_DIXIE_APPROVED) & (~FINAL__SPLIT);
  1123.    }
  1124.  
  1125.    /* NOTE: We may have mirror-reflected the setup.  "Mirror" is true if so.  We may need to undo this. */
  1126.  
  1127.    /* If this is the "split sequential" schema and we have not already done so,
  1128.       cause splitting to take place. */
  1129.  
  1130.    if (the_schema == schema_split_sequential && !(final_concepts & FINAL__SPLIT_SEQ_DONE))
  1131.       final_concepts |= FINAL__SPLIT | FINAL__SPLIT_SEQ_DONE;
  1132.  
  1133.    /* If the split concept is still present, do it. */
  1134.  
  1135.    if (final_concepts & FINAL__SPLIT) {
  1136.       map_thing *split_map;
  1137.  
  1138.       final_concepts &= ~FINAL__SPLIT;
  1139.       ss->setupflags |= SETUPFLAG__SAID_SPLIT;
  1140.  
  1141.       /* We can't handle the mirroring, so undo it. */
  1142.       if (mirror) { mirror_this(ss); mirror = FALSE; }
  1143.  
  1144.       if (ss->kind == s2x4) split_map = (*map_lists[s2x2][1])[MPKIND__SPLIT][0];
  1145.       else if (ss->kind == s1x8) split_map = (*map_lists[s1x4][1])[MPKIND__SPLIT][0];
  1146.       else if (ss->kind == s_ptpd) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][0];
  1147.       else if (ss->kind == s_qtag) split_map = (*map_lists[sdmd][1])[MPKIND__SPLIT][1];
  1148.       else if (ss->kind == s2x2) {
  1149.          /* "Split" was given while we are already in a 2x2?  The only way that
  1150.             can be legal is if the word "split" was meant as a modifier for "split square thru"
  1151.             etc., rather than as a virtual-setup concept, or if the "split sequential" schema
  1152.             is in use.  In those cases, some "split approved" flag will still be on. */
  1153.  
  1154.          if (!(final_concepts & (FINAL__SPLIT_SQUARE_APPROVED | FINAL__SPLIT_DIXIE_APPROVED | FINAL__SPLIT_SEQ_DONE)))
  1155.             fail("Split concept is meaningless in a 2x2.");
  1156.  
  1157.          move(ss, parseptr, callspec, final_concepts, qtfudged, result);
  1158.          return;
  1159.       }
  1160.       else
  1161.          fail("Can't do split concept in this setup.");
  1162.  
  1163.       divided_setup_move(ss, parseptr, callspec, final_concepts, split_map, phantest_ok, TRUE, result);
  1164.       return;
  1165.    }
  1166.  
  1167.    tbonetest = 0;
  1168.    if (setup_limits[ss->kind] >= 0) {
  1169.       int j;
  1170.  
  1171.       for (j=0; j<=setup_limits[ss->kind]; j++) tbonetest |= ss->people[j].id1;
  1172.       if (!(tbonetest & 011)) {
  1173.          result->kind = nothing;
  1174.          return;
  1175.       }
  1176.    }
  1177.  
  1178.    /* We can't handle the mirroring unless the schema is by_array, so undo it. */
  1179.  
  1180.    if (the_schema != schema_by_array) {
  1181.       if (mirror) { mirror_this(ss); mirror = FALSE; }
  1182.    }
  1183.  
  1184.    switch (the_schema) {
  1185.       case schema_nothing:
  1186.          if (final_concepts) fail("Illegal concept for this call.");
  1187.          *result = *ss;
  1188.          result->setupflags = ((ss->setupflags & SETUPFLAG__ELONGATE_MASK) / SETUPFLAG__ELONGATE_BIT) * RESULTFLAG__ELONGATE_BIT;
  1189.          break;
  1190.       case schema_matrix:
  1191.          if (final_concepts) fail("Illegal concept for this call.");
  1192.          matrixmove(ss, callspec, result);
  1193.          reinstate_rotation(ss, result);
  1194.          break;
  1195.       case schema_partner_matrix:
  1196.          if (final_concepts) fail("Illegal concept for this call.");
  1197.          partner_matrixmove(ss, callspec, result);
  1198.          reinstate_rotation(ss, result);
  1199.          break;
  1200.       case schema_roll:
  1201.          if (final_concepts) fail("Illegal concept for this call.");
  1202.          rollmove(ss, callspec, result);
  1203.          break;
  1204.       case schema_by_array:
  1205.          /* Dispose of the "left" concept first -- it can only mean mirror.  If it is on,
  1206.             mirroring may already have taken place. */
  1207.  
  1208.          if (final_concepts & FINAL__LEFT) {
  1209.             if (!(callspec->callflags & cflag__left_means_mirror)) fail("Can't do this call 'left'.");
  1210.             if (!mirror) mirror_this(ss);
  1211.             mirror = TRUE;
  1212.             final_concepts &= ~FINAL__LEFT;
  1213.          }
  1214.  
  1215.          /* The "reverse" concept might mean mirror, or it might be genuine. */
  1216.  
  1217.          if ((final_concepts & FINAL__REVERSE) && (callspec->callflags & cflag__reverse_means_mirror)) {
  1218.             /* This "reverse" just means mirror. */
  1219.             if (mirror) fail("Can't do this call 'left' and 'reverse'.");
  1220.             if (!mirror) mirror_this(ss);
  1221.             mirror = TRUE;
  1222.             final_concepts &= ~FINAL__REVERSE;
  1223.          }
  1224.  
  1225.          /* If the "reverse" flag is still set in final_concepts, it means a genuine
  1226.             reverse as in reverse cut/flip the diamond or reverse change-O. */
  1227.  
  1228.          basic_move(ss, parseptr, callspec, final_concepts, tbonetest, qtfudged, result);
  1229.          break;
  1230.       default:
  1231.    
  1232.          /* Must be sequential or some form of concentric. */
  1233.    
  1234.          new_final_concepts = final_concepts;
  1235.    
  1236.          /* We demand that the final concepts that remain be only those in the following list,
  1237.             which includes all of the "heritable" concepts. */
  1238.  
  1239.          if (new_final_concepts &
  1240.                ~(FINAL__SPLIT | HERITABLE_FLAG_MASK | FINAL__SPLIT_SQUARE_APPROVED | FINAL__SPLIT_DIXIE_APPROVED | FINAL__SPLIT_SEQ_DONE))
  1241.             fail("This concept not allowed here.");
  1242.  
  1243.          /* Now we demand that, if the concept was given, the call had the appropriate flag set saying
  1244.             that the concept is legal and will be inherited to the children.  We make heavy use here
  1245.             of the fact that the "FINAL__XXX" bits and the "cflag__xxx" bits match. */
  1246.  
  1247.          if (HERITABLE_FLAG_MASK & new_final_concepts & (~callspec->callflags)) fail("Can't do this call with this concept.");
  1248.  
  1249.          if (the_schema == schema_sequential || the_schema == schema_split_sequential) {
  1250.             int current_elongation = ss->setupflags & SETUPFLAG__ELONGATE_MASK;
  1251.             int finalsetupflags = 0;
  1252.             int highlimit;
  1253.             long_boolean instant_stop;
  1254.  
  1255.             qtf = qtfudged;
  1256.  
  1257.             if (new_final_concepts & FINAL__SPLIT) {
  1258.                if (callspec->callflags & cflag__split_like_square_thru)
  1259.                   new_final_concepts |= FINAL__SPLIT_SQUARE_APPROVED;
  1260.                else if (callspec->callflags & cflag__split_like_dixie_style)
  1261.                   new_final_concepts |= FINAL__SPLIT_DIXIE_APPROVED;
  1262.             }
  1263.  
  1264.             /* Iterate over the parts of the call. */
  1265.  
  1266.             highlimit = 1000000;
  1267.             subcall_index = 0;          /* Where we start, in the absence of special stuff. */
  1268.             instant_stop = FALSE;
  1269.  
  1270.             /* If SETUPFLAG__FRACTIONALIZE_MASK stuff is nonzero, we are being asked to do something special.
  1271.                Read the three indicators.  Their meaning is as follows:
  1272.                   high 3 bits   middle 3 bits   low 3 bits
  1273.                      "key"       "denom"          "numer"
  1274.                        2          zero            nonzero       do just the indicated part, set
  1275.                                                                     RESULTFLAG__DID_LAST_PART if it was last
  1276.                      zero        nonzero          nonzero       fractional - do first <numer>/<denom> of the call */
  1277.  
  1278.             if (ss->setupflags & SETUPFLAG__FRACTIONALIZE_MASK) {
  1279.                int numer = ((ss->setupflags & (SETUPFLAG__FRACTIONALIZE_BIT*07))   / SETUPFLAG__FRACTIONALIZE_BIT);
  1280.                int denom = ((ss->setupflags & (SETUPFLAG__FRACTIONALIZE_BIT*070))  / (SETUPFLAG__FRACTIONALIZE_BIT*8));
  1281.                int key   = ((ss->setupflags & (SETUPFLAG__FRACTIONALIZE_BIT*0700)) / (SETUPFLAG__FRACTIONALIZE_BIT*64));
  1282.                int t;
  1283.  
  1284.                for (t=0 ; callspec->stuff.def.defarray[t].call_id; t++);   /* Now t = number of parts in call. */
  1285.  
  1286.                if (key == 2) {
  1287.                   /* Just do the "numer" part of the call, and tell if it was last. */
  1288.                   subcall_index = numer-1;
  1289.                   if (subcall_index >= t) fail("The indicated part number doesn't exist.");
  1290.                   instant_stop = TRUE;
  1291.                   /* If only first part is visible, this is illegal unless we are doing first part. */
  1292.                   if (!(callspec->callflags & cflag__visible_fractions) && (subcall_index != 0))
  1293.                      fail("This call can't be fractionalized.");
  1294.                }
  1295.                else {
  1296.                   /* Do parts up to (key=0) or after (key=1) the indicated part.  The indicated
  1297.                      part may be an absolute part number (denom=0) or a fraction. */
  1298.  
  1299.                   int indicated_part;
  1300.  
  1301.                   if (denom) {
  1302.                      /* Amount to do was given as a fraction. */
  1303.                      if (numer >= denom) fail("Fraction must be proper.");
  1304.                      indicated_part = t * numer;
  1305.                      if ((indicated_part % denom) != 0) fail("This call can't be fractionalized with this fraction.");
  1306.                      indicated_part = indicated_part / denom;
  1307.                      /* If only first part is visible, this is illegal. */
  1308.                      if (!(callspec->callflags & cflag__visible_fractions))
  1309.                         fail("This call can't be fractionalized.");
  1310.                   }
  1311.                   else {
  1312.                      indicated_part = numer;
  1313.                      /* If only first part is visible, this is illegal unless we are breaking just after the first part. */
  1314.                      if (!(callspec->callflags & cflag__visible_fractions) && (indicated_part != 1))
  1315.                         fail("This call can't be fractionalized.");
  1316.                   }
  1317.  
  1318.                   if (key == 1) {
  1319.                      /* Do the last section of the call, starting just after indicated_part. */
  1320.                      subcall_index = indicated_part;
  1321.                      if (subcall_index > t) fail("The indicated part number doesn't exist.");
  1322.                   }
  1323.                   else {
  1324.                      /* Do the first section of the call, up to the indicated_part. */
  1325.                      highlimit = indicated_part;
  1326.                      if (highlimit > t) fail("The indicated part number doesn't exist.");
  1327.                   }
  1328.                }
  1329.             }
  1330.  
  1331.             /* Did we neglect to do the touch/rear back stuff because fractionalization was enabled?
  1332.                If so, now is the time to correct that.  We only do it for the first part. */
  1333.  
  1334.             if ((!(ss->setupflags & SETUPFLAG__NO_STEP_TO_WAVE)) && (subcall_index == 0) &&
  1335.                   (callspec->callflags & (cflag__rear_back_from_r_wave | cflag__rear_back_from_qtag | cflag__step_to_wave))) {
  1336.  
  1337.                ss->setupflags |= SETUPFLAG__NO_STEP_TO_WAVE;  /* Can only do it once. */
  1338.  
  1339.                if (final_concepts & FINAL__LEFT) {
  1340.                   mirror = TRUE;
  1341.                   mirror_this(ss);
  1342.                }
  1343.  
  1344.                if (setup_limits[ss->kind] >= 0)          /* We don't understand absurd setups. */
  1345.                   touch_or_rear_back(ss, callspec->callflags);
  1346.             }
  1347.  
  1348.             *result = *ss;
  1349.  
  1350.             for (;;) {
  1351.                int j;
  1352.                setup tttt;
  1353.                by_def_item *this_item;
  1354.                defmodset this_mod;
  1355.  
  1356.                if (subcall_index >= highlimit) break;
  1357.  
  1358.                this_item = &callspec->stuff.def.defarray[subcall_index];
  1359.  
  1360.                if (!this_item->call_id) break;
  1361.  
  1362.                this_mod = this_item->modifiers;
  1363.  
  1364.                temp_concepts = get_mods_for_subcall(new_final_concepts, this_mod, callspec->callflags);
  1365.                get_real_subcall(parseptr, this_item, temp_concepts, &cp1, &call1, &conc1);
  1366.  
  1367.                /* If this context requires a tagging or scoot call, pass that fact on. */
  1368.                if (dfm_must_be_tag_call & this_mod) conc1 |= FINAL__MUST_BE_TAG;
  1369.                else if (dfm_must_be_scoot_call & this_mod) conc1 |= FINAL__MUST_BE_SCOOT;
  1370.  
  1371.                if (dfm_repeat_n & this_mod) {
  1372.                   tempsetup = *result;
  1373.                   for (j=1; j<=parseptr->number; j++) {
  1374.                      tttt = tempsetup;
  1375.                      tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1376.                      move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1377.                      finalsetupflags |= tempsetup.setupflags;
  1378.                   }
  1379.                }
  1380.                else if (dfm_repeat_nm1 & this_mod) {
  1381.                   tempsetup = *result;
  1382.                   for (j=1; j<=parseptr->number-1; j++) {
  1383.                      tttt = tempsetup;
  1384.                      tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1385.                      move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1386.                      finalsetupflags |= tempsetup.setupflags;
  1387.                   }
  1388.                }
  1389.                else if (dfm_repeat_n_alternate & this_mod) {
  1390.                   tempsetup = *result;
  1391.  
  1392.                   /* Read the call after this one -- we will alternate between the two. */
  1393.                   get_real_subcall(parseptr, &callspec->stuff.def.defarray[subcall_index+1], temp_concepts, &cp2, &call2, &conc2);
  1394.  
  1395.                   for (j=1; j<=parseptr->number; j++) {
  1396.                      tttt = tempsetup;
  1397.                      tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1398.                      if (j&1)
  1399.                         move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1400.                      else
  1401.                         move(&tttt, cp2, call2, conc2, qtf, &tempsetup);
  1402.                      finalsetupflags |= tempsetup.setupflags;
  1403.                   }
  1404.                   subcall_index++;     /* Skip over the second call. */
  1405.                }
  1406.                else {
  1407.                   tttt = *result;
  1408.                   tttt.setupflags = (ss->setupflags & ~(SETUPFLAG__FRACTIONALIZE_MASK | SETUPFLAG__ELONGATE_MASK)) | current_elongation;
  1409.  
  1410.                   if ((dfm_cpls_unless_single & this_mod) && !(new_final_concepts & FINAL__SINGLE)) {
  1411.                      tandem_couples_move(&tttt, cp1, call1, conc1,
  1412.                            selector_uninitialized, FALSE, FALSE, 1, &tempsetup);
  1413.                   }
  1414.                   else
  1415.                      move(&tttt, cp1, call1, conc1, qtf, &tempsetup);
  1416.  
  1417.                   finalsetupflags |= tempsetup.setupflags;
  1418.                }
  1419.  
  1420.                /* If this call is "roll transparent", restore roll info from before the call for those people
  1421.                   that are marked as roll-neutral. */
  1422.  
  1423.                if (dfm_roll_transparent & this_mod) {
  1424.                   /* Can only do this if we understand the setups. */
  1425.                   if ((setup_limits[tempsetup.kind] >= 0) && (setup_limits[result->kind] >= 0)) {
  1426.                      int u, v;
  1427.  
  1428.                      for (u=0; u<=setup_limits[tempsetup.kind]; u++) {
  1429.                         if (tempsetup.people[u].id1 & ROLLBITM) {
  1430.                            /* This person is roll-neutral.  Reinstate his original roll info, by
  1431.                               searching for him in the starting setup. */
  1432.                            tempsetup.people[u].id1 &= ~ROLLBITS;
  1433.                            for (v=0; v<=setup_limits[result->kind]; v++) {
  1434.                               if (((tempsetup.people[u].id1 ^ result->people[v].id1) & 0700) == 0)
  1435.                                  tempsetup.people[u].id1 |= (result->people[v].id1 & ROLLBITS);
  1436.                            }
  1437.                         }
  1438.                      }
  1439.                   }
  1440.                }
  1441.  
  1442.                /* Whenever the ending setup is 2x2, we are responsible for computing the
  1443.                   "current_elongation" field of the result. */
  1444.  
  1445.                /* The following comments used to be in this code, back when it was much
  1446.                   more complicated than it is now. */
  1447.  
  1448.                /* If the setup has undergone a transition from a 1x4/diamond to a 2x2, make a note
  1449.                   of what the elongation should be according to the call's "parallel_conc_end" flag.
  1450.                   Remember that "result" contatins the setup at the start of this step, and "tempsetup"
  1451.                   has the setup at the end of this step.  The xor's with the rotations are done to
  1452.                   compensate for the fact that we may have done, say, a lockit, in an earlier step.
  1453.                   The final value that we have to provide for result->parallel_conc_end must state
  1454.                   the elongation relative to the original 1x4 orientation.  Whew!
  1455.                I believe the test cases for this stuff are locker's choice and split cast, the
  1456.                   latter being done from a grand line.  In that case, the ends have to hinge and
  1457.                   then trade.  The hinge has them stay close together, so they are as if in columns.
  1458.                   The trade then must preserve that.
  1459.                This used to xor the current setup rotation and the original setup rotation into the
  1460.                   equation, believing that, if a lockit happened earlier, we want the
  1461.                   "parallel_conc_end" property on the 1x4->2x2 subcall to apply to the entire
  1462.                   call.  I no longer believe that is correct.  What happens now is that each subcall is
  1463.                   evaluated independently.  If a 1x4->2x2 subcall occurs, we find out, from looking
  1464.                   at the call descriptor, where the dancers would like to finish that subcall,
  1465.                   and proceed from there. */
  1466.  
  1467.                /* Next part: we have to preserve the meaning of the "parallel_conc_end" flag
  1468.                   for 2x2->2x2 calls also.  Its meaning in this case is "go to antispots".
  1469.                   This is necessary to make "ENDS counter rotate" work from lines.  (Note
  1470.                   that counter rotate has this flag set.)  For a compound call, we obviously
  1471.                   want to xor all the flags of the constituent parts.  That is why we do an
  1472.                   xor here.  (Note that we initialized it to zero.)  But the stuff just above
  1473.                   overwrote it.  That's OK.  Any 1x4->2x2 call ought to destroy the "antispots"
  1474.                   info from previous 2x2->2x2 calls.  But after the 1x4->2x2 call has filled
  1475.                   in the elongation info, subsequent 2x2->2x2 calls requesting antispots should
  1476.                   flip the elongation.
  1477.                The test case for this is N/4 chain and circulate in.  The ends do a double
  1478.                   circulate, which is defined sequentially as 2 circulates.  Before this code was
  1479.                   put in, basic_move honored the parallel_conc_end/antispots flag for atomic
  1480.                   2x2->2x2 calls, but it wasn't honored here for concatenations of such.  The
  1481.                   final state of result->parallel_conc_end was random, so it would sometimes
  1482.                   think the ends should go to column spots. */
  1483.  
  1484.                if (tempsetup.kind == s2x2) {
  1485.                   switch (result->kind) {
  1486.                      case s1x4: case sdmd: case s2x2:
  1487.                         current_elongation = (((tempsetup.setupflags & RESULTFLAG__ELONGATE_MASK) / RESULTFLAG__ELONGATE_BIT) * SETUPFLAG__ELONGATE_BIT);
  1488.                         break;
  1489.    
  1490.                      /* Otherwise (perhaps the setup was a star) we have no idea how to elongate the setup. */
  1491.    
  1492.                      default:
  1493.                         current_elongation = 0;
  1494.                         break;
  1495.                   }
  1496.                }
  1497.                else
  1498.                   current_elongation = 0;
  1499.  
  1500.                *result = tempsetup;
  1501.  
  1502.                /* Remove outboard phantoms. 
  1503.                   It used to be that normalize_setup was not called
  1504.                   here.  It was found that we couldn't do things like, from a suitable offset wave,
  1505.                   [triple line 1/2 flip] back to a wave, that is, start offset and finish normally.
  1506.                   So this has been added.  However, there may have been a reason for not normalizing.
  1507.                   If any problems are found, it may be that a flag needs to be added to seqdef calls
  1508.                   saying whether to remove outboard phantoms after each part. */
  1509.  
  1510.                normalize_setup(result, simple_normalize);
  1511.  
  1512.                qtf = FALSE;
  1513.  
  1514.                new_final_concepts &= ~(FINAL__SPLIT | FINAL__SPLIT_SQUARE_APPROVED | FINAL__SPLIT_DIXIE_APPROVED);
  1515.  
  1516.                subcall_index++;
  1517.  
  1518.                /* If we are being asked to do just one part of a call (from SETUPFLAG__FRACTIONALIZE_MASK),
  1519.                   exit now.  Also, see if we just did the last part. */
  1520.  
  1521.                if ((ss->setupflags & SETUPFLAG__FRACTIONALIZE_MASK) && instant_stop) {
  1522.                   /* Check whether this is last part of the call, by peeking ahead. */
  1523.                   if (!(&callspec->stuff.def.defarray[subcall_index])->call_id) finalsetupflags |= RESULTFLAG__DID_LAST_PART;
  1524.                   break;
  1525.                }
  1526.             }
  1527.  
  1528.             result->setupflags = (finalsetupflags & ~RESULTFLAG__ELONGATE_MASK) | ((current_elongation / SETUPFLAG__ELONGATE_BIT) * RESULTFLAG__ELONGATE_BIT);
  1529.          }
  1530.          else {
  1531.  
  1532.             /* Must be some form of concentric. */
  1533.    
  1534.             saved_warnings = history[history_ptr+1].warnings;
  1535.  
  1536.             temp_concepts = get_mods_for_subcall(new_final_concepts, callspec->stuff.conc.innerdef.modifiers, callspec->callflags);
  1537.             get_real_subcall(parseptr, &callspec->stuff.conc.innerdef, temp_concepts, &cp1, &call1, &conc1);
  1538.  
  1539.             /* Do it again. */
  1540.  
  1541.             temp_concepts = get_mods_for_subcall(new_final_concepts, callspec->stuff.conc.outerdef.modifiers, callspec->callflags);
  1542.             get_real_subcall(parseptr, &callspec->stuff.conc.outerdef, temp_concepts, &cp2, &call2, &conc2);
  1543.  
  1544.             /* Fudge a 3x4 into a 1/4-tag if appropriate. */
  1545.  
  1546.             if (ss->kind == s3x4 && (callspec->callflags & cflag__fudge_to_q_tag) &&
  1547.                   (the_schema == schema_concentric || the_schema == schema_cross_concentric)) {
  1548.  
  1549.                if (ss->people[0].id1) {
  1550.                   if (ss->people[1].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1551.                }
  1552.                else (void) copy_person(ss, 0, ss, 1);
  1553.  
  1554.                if (ss->people[3].id1) {
  1555.                   if (ss->people[2].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1556.                   else (void) copy_person(ss, 1, ss, 3);
  1557.                }
  1558.                else (void) copy_person(ss, 1, ss, 2);
  1559.  
  1560.                (void) copy_person(ss, 2, ss, 4);
  1561.                (void) copy_person(ss, 3, ss, 5);
  1562.  
  1563.                if (ss->people[6].id1) {
  1564.                   if (ss->people[7].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1565.                   else (void) copy_person(ss, 4, ss, 6);
  1566.                }
  1567.                else (void) copy_person(ss, 4, ss, 7);
  1568.  
  1569.                if (ss->people[9].id1) {
  1570.                   if (ss->people[8].id1) fail("Can't do this call from arbitrary 3x4 setup.");
  1571.                   else (void) copy_person(ss, 5, ss, 9);
  1572.                }
  1573.                else (void) copy_person(ss, 5, ss, 8);
  1574.  
  1575.                (void) copy_person(ss, 6, ss, 10);
  1576.                (void) copy_person(ss, 7, ss, 11);
  1577.  
  1578.                ss->kind = s_qtag;
  1579.                ss->setupflags |= SETUPFLAG__DISTORTED;
  1580.             }
  1581.  
  1582.             concentric_move(
  1583.                ss,
  1584.                cp1, cp2, call1, call2, conc1, conc2,
  1585.                the_schema,
  1586.                callspec->stuff.conc.innerdef.modifiers,
  1587.                callspec->stuff.conc.outerdef.modifiers,
  1588.                result);
  1589.  
  1590.             if (dfm_suppress_elongation_warnings & callspec->stuff.conc.outerdef.modifiers) {
  1591.                history[history_ptr+1].warnings.bits[0] &= ~(Warnings_About_Conc_elongation);
  1592.             }
  1593.             history[history_ptr+1].warnings.bits[0] |= saved_warnings.bits[0];
  1594.             history[history_ptr+1].warnings.bits[1] |= saved_warnings.bits[1];
  1595.          }
  1596.  
  1597.          break;
  1598.    }
  1599.  
  1600.    /* Reflect back if necessary. */
  1601.    if (mirror) mirror_this(result);
  1602.  
  1603.    norot:
  1604.  
  1605.    canonicalize_rotation(result);
  1606. }
  1607.